using System;
using System.Collections.Generic;
using System.Text;
using OWED.Document;
using System.Drawing;
using System.Xml;
using ClientGUI.Modules.Drawing;



namespace ClientGUI
{

    /// <summary>
    /// This is created by Para in its Condense function.
    /// It represents one block of text, including marked text and 
    /// non-marked text.  Until there is a non-ITextAble item.
    /// </summary>
    class TextDraw:Module, ITextAble
    {
        Word [] my_words;
        float line_space = 1.5f;
        int word_space = 3;
        EditableTextDisplayArea my_selected_display_area;
        BitmapDisplayArea my_display;
        BitmapDisplayArea my_hover_display;
        Boolean dirty;
        int my_xml_node_length;

        /// <param name="doc">This module's section of the document</param>
        /// <param name="parent">This module's parent in the tree.</param>
        /// <param name="host">The object which will be drawing this object.</param>
        /// <param name="modconfig">The chunk of the xml config file relating to this document</param>
        /// <param name="tagconfig">The tag configuration object to be associated with this module</param>
        public TextDraw(XmlNode doc, Module parent,
            DisplayDocument host, XmlNode modconfig,
            TagConfig tagconfig)
            :base(doc, parent, host, tagconfig)
        {
            my_xml_node_length = 1;
            if ((doc as XmlText) == null)
            {
                my_words = new Word[1];
                my_words[0] = new Word(" ");
            }
            else
            {
                XmlText doctxt = doc as XmlText;
                String[] strings = doctxt.Value.Split(' ', '\t', '\n', '\r');

                Word [] words = new Word[strings.Length];
                for (int i = 0; i < strings.Length; ++i)
                {
                    words[i] = new Word(strings[i]);
                }
                Filter(words);
            }
            dirty = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="words">The Word objects representing the text for this</param>
        /// <param name="parent">This objects parent</param>
        /// <param name="host">Display host</param>
        /// <param name="tagconfig">TagConfig</param>
        /// <param name="node">The FIRST XmlNode that this represents</param>
        /// <param name="length">The number of XmlNode's that this represents</param>
        public TextDraw(Word [] words, Module parent, DisplayDocument host,
                    TagConfig tagconfig, XmlNode node, int length)
            : base(node, parent, host, tagconfig)
        {
            my_xml_node_length = length;
            Filter(words);
            dirty = true;
        }

        /// <summary>
        /// Side effect:  my_words is set to the filtered copy of words.
        /// Filtered means removing extraneous characters for display purposes.
        /// </summary>
        private void Filter(Word [] words)
        {
            List<Word> filtered = new List<Word>();

            for (int i = 0; i < words.Length; ++i)
            {
                if (words[i].Text != "")
                    filtered.Add(words[i]);

            }
            my_words = filtered.ToArray();
        }

        /// <summary>
        /// Override for the module space routine.  Adds dealing with text.
        /// </summary>
        /// <param name="width">the width allotted.</param>
        public override void Space(int width)
        {
            if (selected)
            {
                dirty = true;
                my_width = width;
                Height = my_selected_display_area.Region.Height;
                return;
            }
            if (dirty || width != my_width)
            {
                dirty = false;
                my_width = width;
                int x = 0;
                int y = 0;
                int max_font_height = 0;
                int adjusted_font_height = 0;
                for (int i = 0; i < my_words.Length; ++i)
                {
                    if (x + my_words[i].Width > Width)
                    {
                        x = 0;
                        y += adjusted_font_height;
                    }
                    x += ((int)my_words[i].Width) + word_space;
                    max_font_height = Math.Max(max_font_height, (int)my_words[i].Height);
                    adjusted_font_height = (int)(max_font_height * line_space);
                }
                if (x > 0)
                    Height = y + adjusted_font_height;
                else
                    Height = y;
                if (my_display != null)
                {
                    my_display.Dispose();
                    my_display = null;
                }
                if (my_hover_display != null)
                {
                    my_hover_display.Dispose();
                    my_hover_display = null;
                }
            }
        }

        private void RenderReg()
        {
            Bitmap my_bitmap = new Bitmap(Width, Height,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            

            Graphics draw = Graphics.FromImage(my_bitmap);
            int x = 0;
            int y = 0;
            int max_font_height = 0;
            int adjusted_font_height = 0;
            
            for (int i = 0; i < my_words.Length; ++i)
            {
                if (x + my_words[i].Width > Width)
                {
                    x = 0;
                    y += adjusted_font_height;
                }
                my_words[i].Draw(draw, x, y);
                x += ((int)my_words[i].Width) + word_space;
                max_font_height = Math.Max(max_font_height, (int)my_words[i].Height);
                adjusted_font_height = (int)(max_font_height * line_space);
            }



            my_display = new BitmapDisplayArea(my_bitmap);
        }

        public void RenderWrap()
        {
            Bitmap my_hover_bitmap = new Bitmap(Width, Height,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            if (my_display == null) RenderReg();

            Bitmap my_bitmap = my_display.BackingBitmap;

            Graphics hover_draw = Graphics.FromImage(my_hover_bitmap);
            hover_draw.DrawImage(my_bitmap, new Point(0, 0));
            hover_draw.DrawRectangle(new Pen(Color.LightGray, 0.1f), new Rectangle(0, 0, Width - 1, Height - 1));

            my_bitmap.Dispose();

            my_hover_display = new BitmapDisplayArea(my_hover_bitmap);
        }

        /// <summary>
        /// Called when this is selected.
        /// We want to display an editable text entry when this happen.
        /// </summary>
        public override void Select()
        {
            base.Select();
            StringBuilder text_build = new StringBuilder();
            foreach (Word w in my_words)
            {
                text_build.Append(w.Text);
                text_build.Append(" ");
            }
            my_selected_display_area = new EditableTextDisplayArea(this, Host, text_build.ToString(), new Rectangle(0, 0, Width, Height +  55));
            Height = Height + 25;
            Host.CalculateSpace();
            Host.Redraw();
        }

        public override int Height
        {
            get
            {
                return base.Height;
            }
            set
            {
                base.Height = value;
            }
        }

        public override List<OWED.Document.ChangeAtom> UnSelect()
        {
            if (my_selected_display_area.Result == EditableTextResult.FINAL)
            {
                String text = my_selected_display_area.Text;
                XmlDocument doc = new XmlDocument();
                ChangeTag atom = new ChangeTag(new AbsolutePath(Node), doc.CreateTextNode(Text),
                                                 doc.CreateTextNode(text));

                //dependent on changes only counting on Modules which
                //are selected.
                
                //First grab all the other children out
                List<Module> before_children = new List<Module>();
                List<Module> after_children = new List<Module>();
                int i;
                for (i = 0; i < Parent.Children.IndexOf(this); ++i)
                {
                    before_children.Add(Parent.Children[i]);
                }
                for (i += my_xml_node_length; i < Parent.Children.Count; ++i)
                {
                    after_children.Add(Parent.Children[i]);
                }

                
                //XmlDocument doc = new XmlDocument();
                String str_doc = "<doc>" + text + "</doc>";
                doc.LoadXml(str_doc);

                /*
                 * This is some hackery which creates a throwaway document to
                 * parse the text which could contain xml data.
                 * It then imports this node into the parent using a function
                 * which should be private.
                 */

               

                XmlNode node = Parent.Node.OwnerDocument.ImportNode(doc.FirstChild, true);
                //assuming RecursiveXMLToModule will fuck the Parent's Children.
                Parent.RecursiveXMLToModule(node, Parent);

                for (int c = before_children.Count - 1; c >= 0; --c)
                {
                    Parent.Children.Insert(0, before_children[c]);
                }
                for (int c = 0; c < after_children.Count; ++c)
                {
                    Parent.Children.Add(after_children[c]);
                }

                List<ChangeAtom> res = new List<ChangeAtom>();
                res.Add(atom);


                selected = false;
                dirty = true; //forcing the display to reallocate space
                Host.CalculateSpace(); //seems to be the only way this works.
                Host.Redraw();
                return res;
            }
            my_selected_display_area.Dispose();
            selected = false;
            dirty = true;
            Host.CalculateSpace();
            selected = true;
            return base.UnSelect(); 
        }

        public override DisplayArea GetDisplayArea()
        {
            if (selected)
            {
                return my_selected_display_area;
            }
            else if (hoovered)
            {
                if (my_hover_display == null) RenderWrap();
                return my_hover_display;
            }
            else
            {
                if (my_display == null) RenderReg();
                return my_display;
            }
        }

        #region ITextAble Members

        public bool Pure
        {
            get { return true; }
        }

        public string Text
        {
            get {
                StringBuilder build = new StringBuilder();
                foreach (Word w in my_words)
                { //TODO:  this is ugly, loses formatting with endlines.
                    build.Append(w.Text);
                    build.Append(" ");
                }
                build.Remove(build.Length - 1, 1);
                return build.ToString() ;
            }
        }

        #endregion

        public override void Dispose()
        {
            base.Dispose();
            my_hover_display.Dispose();
            my_display.Dispose();
            my_selected_display_area.Dispose();
        }
    }
}
